﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Runtime;
using System.ComponentModel;
using System.Windows.Forms;
//using System.Threading;

using RemoteScriptingLibrary.Microsoft.Win32;
          
//using Timer = System.Threading.Timer;
namespace RemoteScriptingLibrary
{
    public class Helper
    {
        protected static Dictionary<IntPtr, string> m_aWindowText = new Dictionary<IntPtr, string>();
        /// <summary>
        /// Delegate for the EnumChildWindows method
        /// </summary>
        /// <param name="hWnd">Window handle</param>
        /// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
        /// <returns>True to continue enumerating, false to bail.</returns>
        public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowProc lpEnumFunc, IntPtr lParam);

        public static Process GetActiveProcess()
        {
            return Process.GetProcessById((int)getActivePID());
        }

        public static IntPtr getActivePID()
        {
            IntPtr hwnd = Win32.GetForegroundWindow();
            uint pid;
            Win32.GetWindowThreadProcessId(hwnd, out pid);
            return (IntPtr)pid;
        }

        public static IntPtr GetWindowFromPartialCaption(string _sText)
        {
            IntPtr ptrWin = IntPtr.Zero;
            ptrWin = Win32.FindWindow(null, null); //PARENT WINDOW
            while (ptrWin != IntPtr.Zero)
            {
                string sText = GetWindowText(ptrWin);

                //m_aWindowText.Add(ptrWin, sText);
                if (sText.ToLower().Contains(_sText.ToLower()))
                {
                    return ptrWin;
                }
                ptrWin = Win32.GetWindow(ptrWin, Win32.GW_HWNDNEXT);
            }
#if DEBUG
            Console.WriteLine("GetWindowFromPartialCaption " + (ptrWin != IntPtr.Zero ? "found "+ptrWin.ToString() : "not found "+IntPtr.Zero.ToString()));
#endif 
            return ptrWin;
        }

        public static void SendCTRLC()
        {
            SendCTRL(Keys.C);
        }


        public static void SendCTRLA()
        {
            SendCTRL(Keys.A);
        }

        public static void SendCTRLV()
        {
            SendCTRL(Keys.V);
        }

        public static void SendCTRLX()
        {
            SendCTRL(Keys.C);
        }

        public static void SendCTRL(Keys _key)
        {
            uint KEYEVENF_KEYUP = 2;
            byte VK_CONTROL = 0x11;

            Win32.keybd_event(VK_CONTROL, 0, 0, 0);

            Win32.keybd_event((byte)_key, 0, 0, 0); //Send the c key (43 is c)
            Win32.keybd_event((byte)_key, 0, KEYEVENF_KEYUP, 0);
            Win32.keybd_event(VK_CONTROL, 0, KEYEVENF_KEYUP, 0); //Left control up
        }

        public static void SendCTRL(IntPtr hwnd, Keys _key)
        {
            Win32.SetForegroundWindow(hwnd);
            SendCTRL(_key);
        }

        public static string GetStringByClassName(IntPtr ptr, string _sClassName)
        {
            _sClassName = _sClassName.ToLower();
            string sText = string.Empty;
            if (_sClassName.Contains("edit"))
            {
                sText = Helper.GetWindowTextRaw(ptr);
            }
            else if (_sClassName.Contains("richedit50w"))
            {
                sText = Helper.GetWindowTextRaw(ptr);
            }
            else if (_sClassName.Contains("listbox"))
            {
                sText = string.Join(Environment.NewLine, Helper.GetListBoxText(ptr));
            }
            return sText;
        }

        public static string[] GetListBoxText(IntPtr ptrWin)
        {
            StringBuilder sb = new StringBuilder(256);
            int cnt = (int)Win32.SendMessage(ptrWin, (uint)Win32.LB.LB_GETCOUNT, IntPtr.Zero, null);
            string[] aText = new string[cnt];
            for (int i = 0; i < cnt; i++)
            {
                IntPtr getText = Win32.SendMessage(ptrWin, (uint)Win32.LB.LB_GETTEXT, i, sb);
                aText[i] = sb.ToString();
            }
            return aText;
        }

        public static string GetListViewText(IntPtr ptrWin)
        {
            Win32.LV_ITEM lvi = new Win32.LV_ITEM();
            lvi.mask = Win32.LVIF_TEXT;
            lvi.cchTextMax = 512;
            lvi.iItem = 1;
            lvi.iSubItem = 0;
            lvi.pszText = Marshal.AllocHGlobal(512);

            IntPtr ptrLvi = Marshal.AllocHGlobal(Marshal.SizeOf(lvi));
            Marshal.StructureToPtr(lvi, ptrLvi, false);

            Win32.SendMessage(ptrWin, Win32.LVM_GETITEM, IntPtr.Zero, ptrLvi);

            string text = Marshal.PtrToStringAuto(lvi.pszText);
            return text;
        }

        public static string GetWindowText(IntPtr ptrWin)
        {
            long nLength = Win32.GetWindowTextLength(ptrWin);
            StringBuilder sbText = new StringBuilder((int)nLength);
            Win32.GetWindowText(ptrWin.ToInt32(), sbText, (int)nLength + 1);

            return sbText.ToString();
        }

        public static string GetWindowTextRaw2(IntPtr hwnd)
        {
            int length = Win32.SendMessageGetTextLength(hwnd, 0x000E, IntPtr.Zero, IntPtr.Zero);
            if (length > 0 && length < int.MaxValue)
            {
                length++;
                StringBuilder sb = new StringBuilder(length);

                Win32.SendMessageGetText(hwnd, Win32.WM_GETTEXT, (IntPtr)sb.Length, sb);
                return sb.ToString();
            }
            return String.Empty;
        }
        
        public string GetLastError()
        {
            string sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
            return sErrorMessage;
        }

        public static void Click(IntPtr ptrHandle, MouseButtons _buttons)
        {
            switch (_buttons)
            {
                case MouseButtons.Left:
                    Win32.SendMessage(ptrHandle.ToInt32(), (int)Win32.WM.LBUTTONDOWN, 0, 0);
                    Win32.SendMessage(ptrHandle.ToInt32(), (int)Win32.WM.LBUTTONUP, 0, 0);
                    break;

                case MouseButtons.Right:
                    Win32.SendMessage(ptrHandle.ToInt32(), (int)Win32.WM.RBUTTONDOWN, 0, 0);
                    Win32.SendMessage(ptrHandle.ToInt32(), (int)Win32.WM.RBUTTONUP, 0, 0);
                    break;

                case MouseButtons.Middle:
                    Win32.SendMessage(ptrHandle.ToInt32(), (int)Win32.WM.MBUTTONDOWN, 0, 0);
                    Win32.SendMessage(ptrHandle.ToInt32(), (int)Win32.WM.MBUTTONUP, 0, 0);
                    break;
            }
        }

        private static bool m_bWaitForWindowFound = false;

        public static bool WaitForWindow(string _sPartialName)
        {
            try
            {
                m_bWaitForWindowFound = false;
                Timer timer = new Timer();
                timer.Tick += delegate(object sender, EventArgs e)
                {
                    IntPtr ptrWindow = GetWindowFromPartialCaption(_sPartialName);
                    if (ptrWindow != IntPtr.Zero)
                    {
                        m_bWaitForWindowFound = true;
                        timer.Stop();
                        return;
                    }
                };
                timer.Start();
            }
            catch (Exception ex)
            {
            }
            return m_bWaitForWindowFound;
        }

        public static string GetControlText(IntPtr hWnd)
        {

            StringBuilder title = new StringBuilder();

            // Get the size of the string required to hold the window title. 
            Int32 size = Win32.SendMessage((int)hWnd, Win32.WM_GETTEXTLENGTH, 0, 0).ToInt32();

            // If the return is 0, there is no title. 
            if (size > 0)
            {
                title = new StringBuilder(size + 1);

                Win32.SendMessage(hWnd, (int)Win32.WM_GETTEXT, title.Capacity, title);


            }
            return title.ToString();
        }

       //public string GetLastError()
       // {
       //     int nLastError = Marshal.GetLastWin32Error();

       //     IntPtr lpMsgBuf = IntPtr.Zero;

       //     uint dwChars = Win32.FormatMessage(
       //         Win32.FORMAT_MESSAGE_ALLOCATE_BUFFER | Win32.FORMAT_MESSAGE_FROM_SYSTEM | Win32.FORMAT_MESSAGE_IGNORE_INSERTS,
       //         IntPtr.Zero,
       //         (uint)nLastError,
       //         0, // Default language
       //         ref lpMsgBuf,
       //         0,
       //         IntPtr.Zero);
       //     if (dwChars == 0)
       //     {
       //         // Handle the error.
       //         int le = Marshal.GetLastWin32Error();
       //         return null;
       //     }

       //     string sRet = Marshal.PtrToStringAnsi(lpMsgBuf);

       //     // Free the buffer.
       //     lpMsgBuf = LocalFree(lpMsgBuf);
       //     return sRet;

       //     // add for the forth signature
       //     try
       //     {
       //         StringBuilder msgBuilder = new StringBuilder(101);

       //         string formatExpression = "%1,%2%!";
       //         string[] formatArgs = new string[] { "Hello", "world" };

       //         IntPtr formatPtr = Marshal.StringToHGlobalAnsi(formatExpression);

       //         //must specify the FORMAT_MESSAGE_ARGUMENT_ARRAY flag when pass an array
       //         uint length = Win32.FormatMessage(Win32.FORMAT_MESSAGE_FROM_STRING | Win32.FORMAT_MESSAGE_ARGUMENT_ARRAY, formatPtr, 0, 0, msgBuilder, 101, formatArgs);

       //         if (length == 0)
       //         {
       //             Win32.FormatMessage(Win32.FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)Marshal.GetLastWin32Error(), 0, msgBuilder, 101, null);

       //             Console.WriteLine("Error:" + msgBuilder.ToString());
       //         }
       //         else
       //         {
       //             Console.WriteLine("Format result:" + msgBuilder.ToString() + ", length:" + length.ToString());
       //         }
       //     }
       //     catch (Exception ex)
       //     {
       //         Console.WriteLine(ex.Message);
       //     }
       // }

        public static Win32.POINT GetMouseCoordinates()
        {
            Win32.POINT point = new Point();
            Win32.GetCursorPos(out point);
            return point;
        }

        public IntPtr GetCurrentControl()
        {
            IntPtr windowHandle = Win32.GetForegroundWindow();
            uint someValue = 0;
            uint threadID = Win32.GetWindowThreadProcessId(windowHandle, out someValue);

            if (Win32.AttachThreadInput(Win32.GetCurrentThreadId(), threadID, true))
            {
                try
                {
                    IntPtr handle = Win32.GetFocus();
                    if (handle != IntPtr.Zero)
                        return handle;
                }
                finally
                {
                    Win32.AttachThreadInput(Win32.GetCurrentThreadId(), threadID, false);
                }
            }
            return IntPtr.Zero;
        }

        public static string GetWindowTextRaw(IntPtr hwnd)
        {
            // Allocate correct string length first
            int length = (int)Win32.SendMessage(hwnd, 0x000E, IntPtr.Zero, IntPtr.Zero);
            StringBuilder sb = new StringBuilder(length + 1);
            Win32.SendMessage(hwnd, Win32.WM_GETTEXT, (IntPtr)sb.Capacity, sb);
            return sb.ToString();
        }

        public static IntPtr GetDacWindow()
        {
            return GetWindowFromPartialCaption("Development Assi");
        }

        public static bool IsWindowTopMost(IntPtr Handle)
        {
            return (Win32.GetWindowLong(Handle, Win32.GWL_EXSTYLE) & Win32.WS_EX_TOPMOST) != 0;
        }

        public static Dictionary<string, IntPtr> GetWindowClassName(IntPtr ptrMainWindow, string _sClassName)
        {
            Dictionary<string, IntPtr> aClassWindowList = new Dictionary<string, System.IntPtr>();
            IntPtr ptrWin = ptrMainWindow;
            while (ptrWin != IntPtr.Zero)
            {
                string sWindowText = GetWindowText(ptrWin);

                StringBuilder sbClassName = new StringBuilder(255);
                Win32.GetClassName(ptrWin, sbClassName, 255);
                string sText = sbClassName.ToString();
                ptrWin = Win32.GetWindow(ptrWin, Win32.GW_CHILD);
                if (sText.ToLower() == _sClassName)
                {
                    aClassWindowList.Add(sText, ptrWin);
                }
            }
            return aClassWindowList;
        }

        /// <summary>
        /// Returns a list of child windows
        /// </summary>
        /// <param name="parent">Parent of the windows to return</param>
        /// <returns>List of child windows</returns>
        public static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }

        public static List<IntPtr> GetWindowByClassName(List<IntPtr> _aList, string _sClassName)
        {
            List<IntPtr> aList = new List<IntPtr>();
#if DEBUG
            foreach(IntPtr handle in _aList)
            {
                StringBuilder sbClassName = new StringBuilder(255);
                Win32.GetClassName(handle, sbClassName, 255);
                string sClassName = sbClassName.ToString();
                if (sClassName.ToLower() == _sClassName.ToLower())
                {
                    aList.Add(handle);
                }
            }
#endif
            return aList;
        }
                     
        /// <summary>
        /// Callback method to be used when enumerating windows.
        /// </summary>
        /// <param name="handle">Handle of the next window</param>
        /// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
        /// <returns>True to continue the enumeration, false to bail</returns>
        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            }

#if DEBUG
            StringBuilder sbClassName = new StringBuilder(255);
            Win32.GetClassName(handle, sbClassName, 255);
            string sClassName = sbClassName.ToString();
#endif
            list.Add(handle);
            //  You can modify this to check to see if you want to cancel the operation, then return a null here
            return true;
        }

        public void FindMenuItem(string _sTitle)
        {
            MENUITEMINFO mif = new MENUITEMINFO();
            mif.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO));
            mif.fMask = 0x10;
            mif.fType = 0;
            mif.dwTypeData = null;
            //bool a = Win32.GetMenuItemInfo(hMenu, 0, true, ref mif);
        }
    }
}
